package login

import (
	"auto-login/xencode"
	"bytes"
	"crypto/aes"
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"time"
)

const (
	pwdKey = "f8*2/h&e3^8%$qp@#39j+c=9-[\"\x08h\x09]\xef"
)

func getMD5(data []byte, key []byte) string {
	if key == nil {
		res := md5.Sum(data)
		return hex.EncodeToString(res[:])
	}
	h := hmac.New(md5.New, key)
	h.Write(data)
	res := h.Sum(nil)
	return hex.EncodeToString(res)
}

func unwrapJsonp(body []byte) map[string]interface{} {
	var (
		start = 0
		stop  = 0
	)
	for start = range body {
		if body[start] == '(' {
			start++
			break
		}
	}
	for stop = len(body) - 1; stop > start; stop-- {
		if body[stop] == ')' {
			break
		}
	}
	ret := make(map[string]interface{})
	json.Unmarshal(body[start:stop], &ret)
	return ret
}

func generateAuthUrl(token []byte, username string, password string, ac_id string, ip string) string {
	urlTemplate := "http://aaa.uestc.edu.cn/cgi-bin/srun_portal?callback=authOK&action=login&username=%s&password={MD5}%s&ac_id=%s&ip=%s&chksum=%s&info=%s&n=200&type=1&os=Linux&name=Linux&double_stack=0&_=%d"
	info := fmt.Sprintf("{\"username\":\"%s\",\"password\":\"%s\",\"ip\":\"%s\",\"acid\":\"%s\",\"enc_ver\":\"srun_bx1\"}", username, password, ip, ac_id)
	b := base64.NewEncoding("LVoJPiCN2R8G90yg+hmFHuacZ1OWMnrsSTXkYpUq/3dlbfKwv6xztjI7DeBE45QA")
	info = "{SRBX1}" + b.EncodeToString(xencode.XEncode([]byte(info), token))
	hmd5 := getMD5([]byte(password), token)
	token_str := string(token)
	chkstr := token_str + username
	chkstr += token_str + hmd5
	chkstr += token_str + ac_id
	chkstr += token_str + ip
	chkstr += token_str + "200"
	chkstr += token_str + "1"
	chkstr += token_str + info
	chksum_bytes := sha1.Sum([]byte(chkstr))
	chksum := hex.EncodeToString(chksum_bytes[:])
	return fmt.Sprintf(
		urlTemplate,
		url.QueryEscape(username),
		url.QueryEscape(hmd5),
		ac_id,
		ip,
		chksum,
		url.QueryEscape(info),
		time.Now().UnixNano()/1000000,
	)
}

func getChallenge(username string, ip string) string {
	urlTemplate := "http://aaa.uestc.edu.cn/cgi-bin/get_challenge?callback=freakChallenge&username=%s&ip=%s&_=%d"
	url := fmt.Sprintf(urlTemplate, username, ip, time.Now().UnixNano()/1000000)
	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	return unwrapJsonp(body)["challenge"].(string)
}

func login(username string, password string) {
	username = username + "@dx-uestc"
	resp, err := http.Get("http://aaa.uestc.edu.cn/srun_portal_pc?ac_id=1&theme=dx")
	if err != nil {
		panic(err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	startFind := bytes.Index(body, []byte("name=\"user_ip\""))
	if startFind == -1 {
		fmt.Println("can't find input[name=\"user_ip\"]")
		return
	}
	startPos := startFind + bytes.Index(body[startFind:], []byte("value=\"")) + 7
	stopPos := startPos + bytes.Index(body[startPos:], []byte("\""))
	ip := string(body[startPos:stopPos])
	token_str := getChallenge(username, ip)
	authUrl := generateAuthUrl([]byte(token_str), username, password, "1", ip)
	resp, err = http.Get(authUrl)
	if err != nil {
		panic(err)
	}
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	res := unwrapJsonp(body)
	if res["res"] != "ok" {
		fmt.Println("fail login, error message: " + res["error_msg"].(string) + "\nerror: " + res["error"].(string))
	}
	fmt.Println(res["suc_msg"])
}

func LoginFromEncrypted(username, passwd string) {
	c, err := aes.NewCipher([]byte(pwdKey))
	if err != nil {
		panic(err)
	}

	encrptedPwd, err := hex.DecodeString(passwd)
	if err != nil {
		panic(err)
	}
	buff := make([]byte, 64)
	c.Decrypt(buff, encrptedPwd)
	stop := bytes.Index(buff, []byte{0})
	password := string(buff[:stop])
	login(username, password)
}

func LoginFromJson() {
	fd, err := os.Open("login.json")
	if err != nil {
		panic(err)
	}
	defer fd.Close()
	confBytes, err := ioutil.ReadAll(fd)
	if err != nil {
		panic(err)
	}

	conf := make(map[string]string)
	err = json.Unmarshal(confBytes, &conf)
	if err != nil {
		panic(err)
	}

	username := conf["username"]
	password := conf["password"]
	LoginFromEncrypted(username, password)
}
